package multi_thread.master.executors;

import java.util.concurrent.*;

/**
 * @author Cheng Jun
 * Description: 如果提交了 Callable，如何获取对应的结果？
 * 将 sumbit(callable) 方法返回的对象 ，全部放到 List 中，然后 逐个遍历 调用其 get 方法
 * 这样会有一个问题，遍历是有先后顺序的。先提交的任务，不一定先执行完！我们需要的是，先执行完的任务，先被 get
 * CompletionService 接口的 实现类  就能实现这个功能。
 * ExecutorCompletionService 内部维护了一个完成队列（先完成的任务，先进入） completionQueue
 * 相当于对 ExecutorService 的一种增强，这种增强体现在对 任务执行结果的维护上，维护了一个 completionQueue 依次收集完成的任务
 * 异步获取任务的完成情况
 * @version 1.0
 * @date 2022/5/22 18:04
 * @see ExecutorCompletionService
 * 重要知识点
 */
public class ExecutorCompletionServiceExample {

    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        // ExecutorCompletionService<Integer> executorCompletionService = new ExecutorCompletionService<>(fixedThreadPool);
        // 也支持传入 completionQueue 的构造函数
        LinkedBlockingQueue<Future<String>> completionQueue = new LinkedBlockingQueue<>();
        ExecutorCompletionService<String> executorCompletionService = new ExecutorCompletionService<>(fixedThreadPool, completionQueue);
        for (int i = 0; i < 5; i++) {
            int j = i;
            Callable<String> randomTask = () -> {
                // 让先提交的任务，耗时更长
                TimeUnit.SECONDS.sleep(5 - j);
                return Thread.currentThread().getName() + " " + j;
            };
            executorCompletionService.submit(randomTask);
        }
        try {
            // 注意这种获取结果不行，此时 completionQueue 中没有已经完成的任务
            // for (Future<Integer> future : completionQueue) {
            //     System.out.println(future.get());
            // }

            // 下面这种方式，是在提前早知道任务数量的情况下，获取 future 的返回值
            // for (int i = 0; i < 5; i++) {
            //     Future<Integer> futureTask = executorCompletionService.take();
            //     // 先完成的任务，先被 take 出来
            //     System.out.println(futureTask.get());
            // }

            // while 方式更优雅一些，如果开发中，请务必使用 poll(long timeout, TimeUnit unit)
            //获取 谁先执行完 谁先出来 take() 获取时候回阻塞  也可以通过Poll方法获取
            // take 方法的注释：Retrieves and removes the Future representing the next completed task, waiting if none are yet present.
            // 检索并删除表示下一个完成任务的Future，如果没有，则等待。但是如果所有的任务都运行完了，下一次take 就会一直阻塞
            Future<String> future;
            // while ((future = executorCompletionService.take()) != null) {
            //     System.out.println(future.get());
            // }
            // 最长的等待时间 五秒，如果五秒内没有获取到 完成的任务，就返回 null
            while ((future = executorCompletionService.poll(5, TimeUnit.SECONDS)) != null) {
                System.out.println(future.get());
            }

            System.out.println("+++++++++++");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("-----------");
        fixedThreadPool.shutdown();
    }
}
